
#include <sudoku.h>
#include <sdk_list.h>

#include <stdlib.h>

sdk_list *_sdk_init_elem_list();

int sdk_solve_square(sudoku *sdk, int row, int col){

  int sol;

  if (sol = _sdk_force_by_list(sdk, row, col) != 0){
    return sol;
  } else if (sol = _sdk_force_to_num(sdk, row, col) != 0){
    return sol;
  } else return 0;

}

int _sdk_force_by_list(sudoku *sdk, int row, int col){

  sdk_list *num_list = _sdk_init_elem_list();

  int i = 1;
  for ( ; i < 10; i++){
    if (_sdk_eliminate_num(i, sdk, row, col)){
      sdk_list_remove_num(num_list, i);
    }
  }

  if (sdk_list_length(num_list) == 1){ // We have a single num that works here.
    return sdk_list_get_elem(num_list, 0)->num;
  }

  return 0;

}

int _sdk_eliminate_num(int num, sudoku *sdk, int row, int col){

  if (_sdk_elim_by_submtx(num, sdk, row, col)){
    return 1;
  } else if (_sdk_elim_by_rc(num, sdk, row, col)){
    return 1;
  }

  return 0;

}

int _sdk_elim_by_submtx(int num, sudoku *sdk, int row, int col){

  vm_matrix *submtx = sdk_enclosing_matrix(sdk, row, col);
  
  int i, j;
  for ( i = 0; i < 3; i++){
    for ( j = 0; j < 3; j++){
      if (((int) submtx->data[i][j]) == num) return 1;
    }
  }

  vm_free_matrix(submtx);

  return 0;

}

int _sdk_elim_by_rc(int num, sudoku *sdk, int row, int col){

  // Start by looking at the row we are in for the number we are passed.
  // If we find that number then we have eliminated the passed num.
  // Then do the same for the column we are in.
  
  int colc = 0;
  
  for ( ; colc < 9; colc++){
    if (SDK_GET_ELEM(sdk, row, colc) == num){
      return 1;
    }
  }

  int rowc = 0;

  for ( ; rowc < 9; rowc++){
    if (SDK_GET_ELEM(sdk, rowc, col) == num){
      return 1;
    }
  }

  return 0;

}

sdk_list *_sdk_init_elem_list(){

  int i = 1;
  sdk_list *list = (sdk_list *)malloc(sizeof(sdk_list));

  for ( ; i < 10; i++){
    sdk_elem *elem = (sdk_elem *)malloc(sizeof(sdk_elem));
    elem->num = i;
    sdk_list_add(list, elem);
  }

  return list;

}

void _sdk_set_force_mtx_rc(vm_matrix *mtx, int offset, int rc){

  int i = 0;

  for ( ; i < 3; i++){
    if (rc == SDK_ROW){
      mtx->data[offset][i] = 1;
    }
    if (rc == SDK_COLUMN){
      mtx->data[i][offset] = 1;
    }
  }

}

int _sdk_force_num(int num, sudoku *sdk, int row, int col){

  fprintf(stderr, "Forcing [%d:%d] to %d\n", row, col, num);

  vm_matrix *sub_mtx = sdk_sub_matrix(sdk, row, col);
  vm_matrix *force_mtx = vm_make_matrix(3, 3);
  
  int i, j;
  for ( i = 0; i < 3; i++){
    for ( j = 0; j < 3; j++){
      if (sub_mtx->data[i][j] != 0) force_mtx->data[i][j] = 1;
    }
  }

  vm_matrix **rcs = (vm_matrix **)malloc(sizeof(vm_matrix *) * 4);

  int row_offset = row % 3;
  int col_offset = col % 3;

  int r1;
  int r2;
  int c1;
  int c2;

  if (row_offset == 0){
    r1 = 1;
    r2 = 2;
  } else if (row_offset == 1){
    r1 = 0;
    r2 = 2;
  } else if (row_offset == 2){
    r1 = 0;
    r2 = 1;
  }
  if (col_offset == 0){
    c1 = 1;
    c2 = 2;
  } else if (col_offset == 1){
    c1 = 0;
    c2 = 2;
  } else if (col_offset == 2){
    c1 = 0;
    c2 = 1;
  }

  fprintf(stderr, "r1=%d r2=%d c1=%d c2=%d\n", r1, r2, c1, c2);

  rcs[0] = sdk_row(sdk, row - r1);
  rcs[1] = sdk_row(sdk, row - r2);
  rcs[2] = sdk_col(sdk, col - c1);
  rcs[3] = sdk_col(sdk, col - c2);

  for ( i = 0; i < 4; i++){ // Iterate through the rows/columns.
    vm_matrix *mtx = rcs[i];
    for ( j = 0; j < 9; j++){ // Iterate through the row/column.
      if ( mtx->data[0][j] == num){ // Now elim this num from the row
	switch (i){
	  case 0:
	    _sdk_set_force_mtx_rc(force_mtx, r1, SDK_ROW);
	    break;
	  case 1:
	    _sdk_set_force_mtx_rc(force_mtx, r2, SDK_ROW);
	    break;
	  case 2:
	    _sdk_set_force_mtx_rc(force_mtx, c1, SDK_COLUMN);
	    break;
	  case 3:
	    _sdk_set_force_mtx_rc(force_mtx, c2, SDK_COLUMN);
	    break;
	}
      }
    }
  }

  int counter = 0;
  for ( i = 0; i < 3; i++){
    for ( j = 0; j < 3; j++){
      if (force_mtx->data[i][j] == 0) counter++;
    }
  }

  if (counter == 1){ // The passed number must go into the passed square.
    return 1;
  }
  return 0;

}

int _sdk_force_to_num(sudoku *sdk, int row, int col){

  int i;

  for ( i = 1; i < 10; i++){
    if ( _sdk_force_num(i, sdk, row, col)) return i;
  }

}
